Imports System.Collections.Specialized
Imports System.Windows.Forms
Imports Microsoft.Win32
Imports System.Net

''' <summary>
''' Some utilities for file and web IO.
''' </summary>
''' <remarks></remarks>
Public Class IOUtil
  ''' <summary>
  ''' Read argument-value pairs from a textfile, and return collection with them.
  ''' </summary>
  ''' <param name="strFile"></param>
  ''' <returns></returns>
  ''' <remarks>Should be replaced XML files ...</remarks>
  Public Shared Function ReadArgumentValueFile(ByVal strFile As String) As NameValueCollection
    Dim strReadLines() As String, NameValues As New System.Collections.Specialized.NameValueCollection

    If System.IO.File.Exists(strFile) Then
      Try
        FileOpen(5, strFile, OpenMode.Input, OpenAccess.Read)
        Debug.WriteLine("Reading argument-value file " & strFile)
        Do While Not EOF(5)
          'Read lines and put in array of lines
          Dim strText As String = LineInput(5)

          If strText.Length > 0 Then
            Dim iCurrent As Integer
            If strReadLines Is Nothing Then
              ReDim strReadLines(0)
              iCurrent = 0
            Else
              iCurrent = strReadLines.GetUpperBound(0) + 1
            End If
            ReDim Preserve strReadLines(iCurrent)

            strReadLines(iCurrent) = strText
          End If
        Loop

        'Find the values corresponding with those in the strNames
        Dim i As Integer
        For i = 0 To strReadLines.GetUpperBound(0)
          'Go through each line and store in collection ...

          'Parse name and value
          Dim strItems() As String = Split(Trim(strReadLines(i)), "=")
          If strItems.Length = 2 Then
            'Add argument and value
            NameValues.Add(strItems(0), strItems(1))
          End If
        Next

        'close file
        FileClose(5)

        Return NameValues

      Catch e As Exception
        Return Nothing
      End Try
    Else
      Return Nothing
    End If
  End Function

  ''' <summary>
  ''' Write argument-value pairs to a textfile, using a collection
  ''' </summary>
  ''' <param name="strFile"></param>
  ''' <param name="NameValues"></param>
  ''' <returns></returns>
  ''' <remarks>Should be replaced XML files ...</remarks>
  Public Overloads Shared Function WriteArgumentValueFile(ByVal strFile As String, ByRef NameValues As NameValueCollection) As Boolean
    Try
      FileOpen(5, strFile, OpenMode.Output, OpenAccess.Write)
      Debug.WriteLine("Write argument-value file " & strFile)

      'Find the values corresponding with those in the strNames
      Dim i As Integer
      For i = 0 To NameValues.Count - 1
        PrintLine(5, NameValues.GetKey(i) & "=" & NameValues.Get(i))
      Next

      'close file
      FileClose(5)
      Return True
    Catch e As Exception
      Return False
    End Try
  End Function

  ''' <summary>
  ''' Write an array of strings to file.
  ''' </summary>
  ''' <param name="strFile"></param>
  ''' <param name="strNames"></param>
  ''' <returns></returns>
  ''' <remarks>Should be replaced XML files ...</remarks>
  Public Overloads Shared Function WriteArgumentValueFile(ByVal strFile As String, _
                                                        ByVal strNames() As String) As Boolean
    Return WriteArgumentValueFile(strFile, strNames, Nothing)
  End Function

  ''' <summary>
  ''' Write an argument-value file in ASCII. Can also be used to write an array of string by not passing values.
  ''' </summary>
  ''' <param name="strFile"></param>
  ''' <param name="strNames"></param>
  ''' <param name="strValues"></param>
  ''' <returns></returns>
  ''' <remarks>Should be replaced XML files ...</remarks>
  Public Overloads Shared Function WriteArgumentValueFile(ByVal strFile As String, _
                                                          ByVal strNames() As String, _
                                                          ByRef strValues() As String) As Boolean
    'Write argument-value pairs to a textfile
    Try
      FileOpen(5, strFile, OpenMode.Output, OpenAccess.Write)
    Catch e As Exception
      MsgBox("WriteArgumentValueFile:" & e.Message, CType(MsgBoxStyle.Critical + MsgBoxStyle.OkOnly, MsgBoxStyle))
      Return False
    End Try

    Debug.WriteLine("Write argument-value file " & strFile)

    If strValues Is Nothing Then
      For i As Integer = 0 To strNames.GetUpperBound(0)
        PrintLine(5, strNames(i))
      Next
    Else
      For i As Integer = 0 To strNames.GetUpperBound(0)
        PrintLine(5, strNames(i) & "=" & strValues(i))
      Next
    End If

    'close file
    FileClose(5)

    Return True
  End Function

  ''' <summary>
  ''' 
  ''' </summary>
  ''' <param name="strKeyPath"></param>
  ''' <param name="strName"></param>
  ''' <returns></returns>
  ''' <remarks></remarks>
  Public Shared Function ReadUserKey(ByVal strKeyPath As String, ByVal strName As String) As Object
    Dim key As RegistryKey = Registry.CurrentUser.OpenSubKey(strKeyPath)
    If key Is Nothing Then
      Return Nothing
    Else
      Return key.GetValue(strName)
    End If
  End Function

  ''' <summary>
  ''' 
  ''' </summary>
  ''' <param name="strKeyPath"></param>
  ''' <param name="strName"></param>
  ''' <param name="value"></param>
  ''' <remarks></remarks>
  Public Shared Sub WriteUserKey(ByVal strKeyPath As String, ByVal strName As String, ByVal value As Object)
    Dim key As RegistryKey = Registry.CurrentUser.OpenSubKey(strKeyPath, True)
    If key Is Nothing Then
      'Create it
      key = Registry.CurrentUser.CreateSubKey(strKeyPath)
    End If
    key.SetValue(strName, value)
  End Sub

  Public Shared Function GetImageFileType(ByVal strFilename As String) As System.Drawing.Imaging.ImageFormat
    Dim strExt As String = System.IO.Path.GetExtension(strFilename).ToLower
    Select Case strExt
      Case ".jpg", ".jpeg"
        Return System.Drawing.Imaging.ImageFormat.Jpeg
      Case ".png"
        Return System.Drawing.Imaging.ImageFormat.Png
      Case ".tif", ".tiff"
        Return System.Drawing.Imaging.ImageFormat.Tiff
      Case ".bmp"
        Return System.Drawing.Imaging.ImageFormat.Bmp
      Case ".gif"
        Return System.Drawing.Imaging.ImageFormat.Gif
      Case Else
        Throw New ArgumentException("Unkown image file type")
    End Select
  End Function

  ''' <summary>
  ''' Get a webrequest to a given URL and Session.
  ''' </summary>
  ''' <returns></returns>
  ''' <remarks></remarks>
  <System.Net.WebPermission(System.Security.Permissions.SecurityAction.Assert, Unrestricted:=True)> _
  Public Shared Function GetHTTPSessionRequest(ByVal oUri As Uri, ByVal strSessionID As String) As System.Net.HttpWebRequest
    Dim request As HttpWebRequest = CType(WebRequest.Create(oUri), HttpWebRequest)
    If Not strSessionID Is Nothing Then
      If strSessionID.Length > 0 Then
        Dim cookies As New CookieContainer
        Dim cookie As New Cookie
        cookie.Name = "ASP.NET_SessionId"
        cookie.Value = strSessionID
        cookie.Domain = oUri.Host
        cookies.Add(cookie)
        request.CookieContainer = cookies
      End If
    End If
    Return request
  End Function

  ''' <summary>
  ''' Get a HTTP download stream. This is fine when the whole response will only contain the binary data of 1 file of known type.
  ''' </summary>
  ''' <param name="oUri"></param>
  ''' <returns></returns>
  ''' <remarks></remarks>
  <System.Net.WebPermission(System.Security.Permissions.SecurityAction.Assert, Unrestricted:=True)> _
  Public Overloads Shared Function GetHTTPDownloadStream(ByVal oUri As Uri) As System.IO.Stream
    Return GetHTTPDownloadStream(oUri, "")
  End Function

  ''' <summary>
  ''' Get a HTTP download stream. This is fine when the whole response will only contain the binary data of 1 file of known type.
  ''' </summary>
  ''' <param name="oUri"></param>
  ''' <param name="strSessionID"></param>
  ''' <returns></returns>
  ''' <remarks></remarks>
  <System.Net.WebPermission(System.Security.Permissions.SecurityAction.Assert, Unrestricted:=True)> _
  Public Overloads Shared Function GetHTTPDownloadStream(ByVal oUri As Uri, ByVal strSessionID As String) As System.IO.Stream
    Dim oWebrequest As HttpWebRequest = GetHTTPSessionRequest(oUri, strSessionID)
    oWebrequest.Method = "GET"
    Dim oWebResponse As WebResponse = oWebrequest.GetResponse()
    Return oWebResponse.GetResponseStream
  End Function

  ''' <summary>
  ''' Get a HTTP upload request and stream. This is fine when the whole request will only contain the binary data of 1 file of known type (known to .NET).
  ''' For general upload it is better to make sure the file appears in the posted file collection of a request, using UploadHTTPFile().
  ''' </summary>
  ''' <param name="oUri"></param>
  ''' <returns></returns>
  ''' <remarks></remarks>
  <System.Net.WebPermission(System.Security.Permissions.SecurityAction.Assert, Unrestricted:=True)> _
  Public Overloads Shared Function GetHTTPUploadRequest(ByVal oUri As Uri) As HttpWebRequest
    Return GetHTTPUploadRequest(oUri, "")
  End Function

  ''' <summary>
  ''' Get a HTTP upload request and stream. This is fine when the whole request will only contain the binary data of 1 file of known type (known to .NET).
  ''' For general upload it is better to make sure the file appears in the posted file collection of a request, using UploadHTTPFile().
  ''' </summary>
  ''' <param name="oUri"></param>
  ''' <param name="strSessionID"></param>
  ''' <returns></returns>
  ''' <remarks>Returning the stream is not enough as the request needs to call GetResponse to actually trigger the upload!</remarks>
  <System.Net.WebPermission(System.Security.Permissions.SecurityAction.Assert, Unrestricted:=True)> _
  Public Overloads Shared Function GetHTTPUploadRequest(ByVal oUri As Uri, ByVal strSessionID As String) As HttpWebRequest
    Dim oWebrequest As HttpWebRequest = GetHTTPSessionRequest(oUri, strSessionID)
    'Normally method is PUT, but when using encryption we use a special httphandler requiring POST
    Dim strMethod As String = "PUT"
    If oUri.Query.Length > 0 Then strMethod = "POST"
    oWebrequest.Method = strMethod
    Return oWebrequest
  End Function

  ''' <summary>
  ''' Upload a file to certain session on a webserver using HTTP, where it will available in the posted file collection. 
  ''' </summary>
  ''' <param name="imageFilename"></param>
  ''' <param name="uri"></param>
  ''' <param name="sessionID"></param>
  ''' <remarks>This is not the same as dumping a file in a HTTP upload stream, as in that case the file does not appear in the 
  ''' posted file collection. Rather, the whole request IS the the file and extra information (like the filename) needs to be transferred
  ''' in the querystring. </remarks>
  Public Shared Sub UploadHTTPFile(ByVal imageFilename As String, ByVal uri As Uri, ByVal sessionID As String)
    Dim webrequest As HttpWebRequest = GetHTTPSessionRequest(uri, sessionID)
    Dim boundary As String = "----------" + DateTime.Now.Ticks.ToString("x")
    webrequest.ContentType = "multipart/form-data; boundary=" + boundary
    webrequest.Method = "POST"
    Dim sb As New System.Text.StringBuilder
    sb.Append("--")
    sb.Append(boundary)
    sb.Append("" & Microsoft.VisualBasic.Chr(13) & "" & Microsoft.VisualBasic.Chr(10) & "")
    sb.Append("Content-Disposition: form-data; name=""")
    sb.Append("")
    sb.Append("""; filename=""")
    sb.Append(IO.Path.GetFileName(imageFilename))
    sb.Append("""")
    sb.Append("" & Microsoft.VisualBasic.Chr(13) & "" & Microsoft.VisualBasic.Chr(10) & "")
    sb.Append("Content-Type: ")
    sb.Append("application/octet-stream")
    sb.Append("" & Microsoft.VisualBasic.Chr(13) & "" & Microsoft.VisualBasic.Chr(10) & "")
    sb.Append("" & Microsoft.VisualBasic.Chr(13) & "" & Microsoft.VisualBasic.Chr(10) & "")
    Dim postHeader As String = sb.ToString
    Dim postHeaderBytes As Byte() = System.Text.Encoding.UTF8.GetBytes(postHeader)
    Dim boundaryBytes As Byte() = System.Text.Encoding.ASCII.GetBytes("" & Microsoft.VisualBasic.Chr(13) & "" & Microsoft.VisualBasic.Chr(10) & "--" + boundary + "" & Microsoft.VisualBasic.Chr(13) & "" & Microsoft.VisualBasic.Chr(10) & "")
    Dim fileStream As IO.FileStream = New IO.FileStream(imageFilename, IO.FileMode.Open, IO.FileAccess.Read)
    Dim length As Long = postHeaderBytes.Length + fileStream.Length + boundaryBytes.Length
    webrequest.ContentLength = length
    Dim requestStream As IO.Stream = webrequest.GetRequestStream
    requestStream.Write(postHeaderBytes, 0, postHeaderBytes.Length)

    Dim bytesRead, totalBytesRead As Integer, totalBytes As Integer = CInt(fileStream.Length)
    Dim buffer(4095) As Byte
    Do
      bytesRead = fileStream.Read(buffer, 0, 4096)
      If bytesRead > 0 Then requestStream.Write(buffer, 0, bytesRead)
      totalBytesRead += bytesRead
    Loop While totalBytesRead < totalBytes

    requestStream.Write(boundaryBytes, 0, boundaryBytes.Length)
    requestStream.Close()
    Dim response As WebResponse = webrequest.GetResponse
  End Sub

  ''' <summary>
  ''' Upload an image to a website.
  ''' </summary>
  ''' <param name="img"></param>
  ''' <param name="targetFilename"></param>
  ''' <param name="uri"></param>
  ''' <param name="sessionID"></param>
  ''' <remarks></remarks>
  Public Shared Sub UploadHTTPImage(ByVal img As Drawing.Bitmap, ByVal uri As Uri, ByVal sessionID As String)
    Dim webrequest As HttpWebRequest = GetHTTPSessionRequest(uri, sessionID)
    Dim boundary As String = "----------" + DateTime.Now.Ticks.ToString("x")
    webrequest.ContentType = "multipart/form-data; boundary=" + boundary
    webrequest.Method = "POST"
    Dim sb As New System.Text.StringBuilder
    sb.Append("--")
    sb.Append(boundary)
    sb.Append("" & Microsoft.VisualBasic.Chr(13) & "" & Microsoft.VisualBasic.Chr(10) & "")
    sb.Append("Content-Disposition: form-data; name=""")
    sb.Append("")
    sb.Append("""; filename=""")
    sb.Append("image.jpg")
    sb.Append("""")
    sb.Append("" & Microsoft.VisualBasic.Chr(13) & "" & Microsoft.VisualBasic.Chr(10) & "")
    sb.Append("Content-Type: ")
    sb.Append("application/octet-stream")
    sb.Append("" & Microsoft.VisualBasic.Chr(13) & "" & Microsoft.VisualBasic.Chr(10) & "")
    sb.Append("" & Microsoft.VisualBasic.Chr(13) & "" & Microsoft.VisualBasic.Chr(10) & "")
    Dim postHeader As String = sb.ToString
    Dim postHeaderBytes As Byte() = System.Text.Encoding.UTF8.GetBytes(postHeader)
    Dim boundaryBytes As Byte() = System.Text.Encoding.ASCII.GetBytes("" & Microsoft.VisualBasic.Chr(13) & "" & Microsoft.VisualBasic.Chr(10) & "--" + boundary + "" & Microsoft.VisualBasic.Chr(13) & "" & Microsoft.VisualBasic.Chr(10) & "")

    Dim imageBytes() As Byte = ImageToBytes(img, Drawing.Imaging.ImageFormat.Jpeg)

    Dim length As Long = postHeaderBytes.Length + imageBytes.Length + boundaryBytes.Length
    webrequest.ContentLength = length
    Dim requestStream As IO.Stream = webrequest.GetRequestStream
    requestStream.Write(postHeaderBytes, 0, postHeaderBytes.Length)

    requestStream.Write(imageBytes, 0, imageBytes.Length)

    'Chunking, if necessary ...
    'Dim bytesToRead, totalBytesRead As Integer
    'Dim totalBytes As Integer = byteImage.Length
    'Do
    '  bytesToRead = Math.Min(4096, totalBytes - totalBytesRead)
    '  If bytesToRead > 0 Then requestStream.Write(byteImage, totalBytesRead, bytesToRead)
    '  totalBytesRead += bytesToRead
    'Loop While totalBytesRead < totalBytes

    requestStream.Write(boundaryBytes, 0, boundaryBytes.Length)
    requestStream.Close()
    Dim response As WebResponse = webrequest.GetResponse
  End Sub

  Public Shared Function ImageToBytes(ByVal img As Drawing.Bitmap, ByVal format As Drawing.Imaging.ImageFormat) As Byte()
    Dim memoryStream As New IO.MemoryStream
    img.Save(memoryStream, format)
    Dim imageBytes(CInt(memoryStream.Length)) As Byte
    imageBytes = memoryStream.ToArray()
    memoryStream.Close()
    Return imageBytes
  End Function

  Public Shared Function BytesToImage(ByVal imgBytes() As Byte) As Drawing.Bitmap
    Return New Drawing.Bitmap(New IO.MemoryStream(imgBytes))
  End Function

  ''' <summary>
  ''' Multidimensional arrays cannot be serialised, believe it or not. This routine converts a double 2 dimensional array
  ''' to a jagged array, which can be serialised
  ''' </summary>
  ''' <param name="myArray"></param>
  ''' <returns></returns>
  ''' <remarks></remarks>
  Public Function ToJaggedArray(ByVal myArray(,) As Double) As Double()()
    Dim jaggedArray(myArray.GetUpperBound(0))() As Double
    For i As Integer = 0 To myArray.GetUpperBound(0)
      jaggedArray(i) = New Double(myArray.GetUpperBound(1)) {}
      For j As Integer = 0 To myArray.GetUpperBound(1)
        jaggedArray(i)(j) = myArray(i, j)
      Next
    Next
    Return jaggedArray
  End Function

  ''' <summary>
  ''' Multidimensional arrays cannot be serialised, believe it or not. This routine converts a jagged array to a 
  ''' double 2 dimensional array.
  ''' </summary>
  ''' <param name="myArray"></param>
  ''' <returns></returns>
  ''' <remarks></remarks>
  Public Function FromJaggedArray(ByVal jaggedArray()() As Double) As Double(,)
    If jaggedArray.GetUpperBound(0) > 0 Then
      Dim myArray(jaggedArray.GetUpperBound(0), jaggedArray(0).GetUpperBound(0)) As Double
      For i As Integer = 0 To myArray.GetUpperBound(0)
        For j As Integer = 0 To myArray.GetUpperBound(1)
          myArray(i, j) = jaggedArray(i)(j)
        Next
      Next
      Return myArray
    Else
      Return Nothing
    End If
  End Function

End Class
